package cn.gemframe.security.config;

import cn.gemframe.common.constant.RequestPath;
import cn.gemframe.config.security.GemTokenProperties;
import cn.gemframe.security.filter.GemUserLoginValidateFilter;
import cn.gemframe.security.handler.GemLoginFailureHandler;
import cn.gemframe.security.handler.GemLoginSuccessHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Slf4j
@Configuration
public class GemWebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	private Environment env;
	@Autowired
	private TokenStore tokenStore;
	@Autowired
	private GemLoginSuccessHandler loginSuccessHandler;
	@Autowired
	private GemLoginFailureHandler loginFailureHandler;
	@Autowired
	private UserDetailsService userDetailsService;
	@Autowired
	private GemTokenProperties gemTokenProperties;
	@Autowired
	private ValueOperations<String, Object> valueOperations;
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		// 使用BCrypt进行密码的hash
		return new BCryptPasswordEncoder();
	}

//	/**
//	 * 不定义没有password
//	 * grant_type即密码授权模式
//	 * （总共四种授权模式：授权码、implicat精简模式、密码、client credentials）
//	 */
//	@Bean
//	public AuthenticationManager authenticationManagerBean()
//			throws Exception {
//		return super.authenticationManagerBean();
//	}

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}

//	/**
//	 * 如果有要忽略拦截校验的静态资源，在此处添加
//	 * 忽略任何以”/resources/”开头的请求，这和在XML配置http@security=none的效果一样
//	 */
//	public void configure(WebSecurity web) throws Exception {
//		// TODO Auto-generated method stub
//		web.ignoring()
//				.antMatchers("/resources/**");
//	}


	@Override
	protected void configure(HttpSecurity http) throws Exception {
		//用户登录限制的过滤器
		GemUserLoginValidateFilter loginFilter = new GemUserLoginValidateFilter();
		loginFilter.setGemTokenProperties(gemTokenProperties);
		loginFilter.setValueOperations(valueOperations);
		loginFilter.setTokenStore(tokenStore);
		loginFilter.setEnv(env);
		//基本配置
		http
			// 在登陆之前添加用户登录限制的过滤器
			.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class)
			// 设置表单登陆
			.formLogin()
			// 设置登录页（rest风格设置错误的登录页）
			.loginPage(RequestPath.Auth.ERROR)
			// 登陆的方法
			.loginProcessingUrl(RequestPath.Auth.LOGIN)
			// 登陆成功之后需要处理的方法
			.successHandler(loginSuccessHandler)
			// 登陆失败之后需要处理的方法
			.failureHandler(loginFailureHandler)
			//登陆的实现方法
			.and()
			.userDetailsService(userDetailsService)
			//设置请求拦截,不需要拦截的方法
			.authorizeRequests()
                .antMatchers(RequestPath.Auth.LOGIN).permitAll()
                .antMatchers(RequestPath.Auth.ERROR).permitAll()
                .antMatchers(RequestPath.Auth.LOGOUT).permitAll()
                .antMatchers(RequestPath.Auth.REFRESH_TOKEN).permitAll()
			.antMatchers(HttpMethod.OPTIONS).permitAll()
			// 设置拦截任何请求都必须经过认证才能访问
			.anyRequest().authenticated()
			.and()
			// 禁止跨域请求
			.csrf().disable()
			// 进行http Basic认证
//			.httpBasic()
		;
	}
}
